package com.example.demo.config;

import cn.hutool.json.JSONUtil;
import com.example.demo.pojo.Result;
import org.apache.http.entity.ContentType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @author tds
 * @version 1.0.0
 * @Company
 * @ClassName SecurityConfig
 * @Description SpringSecurity配置
 * @Date 2022-03-27 23:52
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * @Description 密码加密方式
     * @param
     * @return org.springframework.security.crypto.password.PasswordEncoder
     */
    @Bean
    public PasswordEncoder pwEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义登录页、授权认证。。。
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表单提交
        http.formLogin()
                // 自定义请求登录名和密码字段名称(默认是username和password)
//                .usernameParameter("username1")
//                .passwordParameter("password1")
                // 当发现/login时认为是登录，必须和表单提交的地址一样，去执行UserDetailServiceImpl
                .loginProcessingUrl("/login")
                // 自定义登录页
                .loginPage("/login.html")
                // 登录成功的跳转页面，这里要求是post请求
                .successForwardUrl("/toMain")
                // 自定义登录成功处理器，和successForwardUrl冲突，二选一。这里用的重定向，适用于前后端分离的项目
                // sucessForwadUrl里面用的转发，所以对于前后端分离项目不适用
//                .successHandler((request, response, authentication) -> response.sendRedirect("https://www.baidu.com"))
                // 登录失败的跳转页面，这里要求是post请求
                .failureForwardUrl("/toError");

        // 授权认证
        http.authorizeRequests()
                // 登录页、登录失败页不需要被认证
                .antMatchers("/login.html", "/error.html").permitAll()
                // 放行所有css或js文件夹下的文件
                .antMatchers("/css/**", "/js/**").permitAll()
                // 放行所有*.js文件
                .antMatchers("/**/*.js").permitAll()
                // 正则匹配的放行规则
//                .regexMatchers(".+[.]png").permitAll()
//                .mvcMatchers("/login.html").servletPath("/contextpath").permitAll()
                // 访问权限控制(控制拥有权限或角色才可以访问某些资源)
//                .antMatchers("/test.html").hasAnyAuthority("admin")
//                .antMatchers("/test.html").hasAuthority("admin")
//                .antMatchers("/test.html").hasRole("abc")
                // access表达式。登录用户权限的方法里面都是基于表达式的。比如hasRole可以用一下写法
//                .antMatchers("/test.html").access("hasRole('abc')")
//                .antMatchers("/test.html").hasAnyRole("abc")
                // ip地址控制
//                .antMatchers("/test.html").hasIpAddress("127.0.0.1")
                // 所有请求都要被认证，登录成功后才能访问
                .anyRequest().authenticated();
                // 自定义权限校验方法：myServiceImpl.hasPerimit
//                 .anyRequest().access("@myServiceImpl.hasPerimit(request, authentication)");
        // 关闭csrf保护
        http.csrf().disable();

        // 访问权限不足时的403自定义处理逻辑
        http.exceptionHandling()
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    // 返回403状态码
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    response.setContentType(ContentType.APPLICATION_JSON.toString());
                    // 返回一个json信息
                    PrintWriter writer = response.getWriter();
                    Result res = Result.error("权限不足，请联系管理员");
                    writer.write(JSONUtil.toJsonStr(res));
                    writer.flush();
                    writer.close();
                });
    }
}
